package lifi

import (
	"context"
	"errors"
)

// Data is a data for process.
type Data interface {
	Group() Group
	Drop()
}

type ByteData struct {
	Data      []byte
	GroupName string
}

func (b ByteData) Drop() {
}

var _ Data = ByteData{}

func (b ByteData) Group() Group {
	return Group(b.GroupName)
}
func NewByteData(data []byte, groupName string) ByteData {
	return ByteData{Data: data, GroupName: groupName}
}

// Group is a group of data.
type Group string

type DataHandler interface {

	// HandleData handles an data.
	HandleData(context.Context, Data) error
}

type DataHandlerFunc func(context.Context, Data) error
type DataBus interface {
	DataHandler

	// AddHandler adds a handler for an event. Returns an error if either the
	// matcher or handler is nil, the handler is already added or there was some
	// other problem adding the handler (for networked handlers for example).
	AddHandler(context.Context, DataMatcher, DataHandler) error

	// Errors returns an error channel where async handling errors are sent.
	Errors() <-chan error

	// Close closes the DataBus and waits for all handlers to finish.
	Close() error
}

type DataMatcher interface {
	// Match returns true if the matcher matches an event.
	Match(data Data) bool
}

type trackingData struct {
	Data
	d *trackingInfo
}

type trackingInfo struct {
	id       TrackingID
	fn       NotifyFunc
	rejected int
}

type deliveryInfo struct {
	id       TrackingID
	rejected int
}

func (d deliveryInfo) ID() TrackingID {
	return d.id
}

func (d deliveryInfo) Delivered() bool {
	return d.rejected == 0
}

func (d *trackingInfo) notify() {
	d.fn(
		&deliveryInfo{
			id:       d.id,
			rejected: d.rejected,
		},
	)
}
func DataWithTracking(m Data, notifyFn func(info DeliveryInfo2)) (Data, TrackingID) {
	rs := &trackingData{Data: m,
		d: &trackingInfo{
			id:       newTrackingID(),
			fn:       notifyFn,
			rejected: 0,
		},
	}

	return rs, rs.d.id
}

type ByteDataSerializer struct {
}

func (b *ByteDataSerializer) Serialize(data Data) ([]byte, error) {
	if byteData, ok := data.(ByteData); ok {
		return byteData.Data, nil
	}
	return nil, errors.New("unknow data")
}

func (b *ByteDataSerializer) SerializeBatch(datas []Data) ([]byte, error) {
	var result []byte
	for _, d := range datas {
		if byteData, ok := d.(ByteData); ok {
			result = append(result, byteData.Data...)
		} else {
			return nil, errors.New("unknow data")
		}
	}
	return result, nil
}

var _ Serializer = &ByteDataSerializer{}
